package cn.lingyangwl.agile.system.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.lingyangwl.agile.auth.*;
import cn.lingyangwl.agile.auth.model.*;
import cn.lingyangwl.agile.auth.model.entity.*;
import cn.lingyangwl.agile.common.security.annotation.*;
import cn.lingyangwl.agile.common.security.constants.*;
import cn.lingyangwl.agile.common.security.utils.*;
import cn.lingyangwl.agile.model.*;
import cn.lingyangwl.agile.model.constants.*;
import cn.lingyangwl.agile.model.enums.*;
import cn.lingyangwl.agile.model.module.auth.*;
import cn.lingyangwl.agile.model.module.config.param.*;
import cn.lingyangwl.agile.model.module.tenant.*;
import cn.lingyangwl.agile.msg.push.api.constatnts.*;
import cn.lingyangwl.agile.msg.push.api.model.rqrs.*;
import cn.lingyangwl.agile.system.assembly.*;
import cn.lingyangwl.agile.system.manager.*;
import cn.lingyangwl.agile.system.mapper.*;
import cn.lingyangwl.agile.system.model.entity.*;
import cn.lingyangwl.agile.system.model.rqrs.config.*;
import cn.lingyangwl.agile.system.model.rqrs.log.*;
import cn.lingyangwl.agile.system.model.rqrs.user.*;
import cn.lingyangwl.agile.system.service.*;
import cn.lingyangwl.framework.data.mate.annotations.*;
import cn.lingyangwl.framework.data.mate.fieldsensitive.*;
import cn.lingyangwl.framework.mybatis.mate.annotations.*;
import cn.lingyangwl.framework.mybatis.mate.enums.*;
import cn.lingyangwl.framework.mybatis.plus.utils.*;
import cn.lingyangwl.framework.tool.core.*;
import cn.lingyangwl.framework.tool.core.exception.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author shenguangyang
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class SysUserServiceImpl implements SysUserService {
    @Resource
    private SysUserMapper userMapper;
    @Resource
    private SysUserDeptRelMapper userDeptRelMapper;
    @Resource
    private SysUserRoleMapper userRoleRelMapper;
    @Resource
    private SysRoleManager roleManager;
    @Resource
    private SysUserAssembly userAssembly;
    @Resource
    private SysConfigService configService;
    @Resource
    private SysUserManager userManager;

    @Resource
    private SysConfigService sysConfigService;
    @Resource
    private AuthApi authApi;

    @Resource
    private SysTenantManager tenantManager;

    @Resource
    private SysMqManager mqManager;
    @Resource
    private AuthorizeService authorizeService;
    @Resource
    private SysRoleAssembly roleAssembly;

    @Resource
    private FieldSensitiveHandler sensitiveHandler;

    @Override
    @DataRule(entityClass = SysUser.class, code = DataRuleCodeEnum.USER_LIST)
    @FieldBindHandle
    public List<UserResp> list(UserPageReq query) {
        // 获取当前应用的租户管理员
        SysTenant tenant = tenantManager.getById(TenantContext.get().getTenantId());
        // 排除租户管理员和当前租户
        Long adminUserId = tenant.getAdminUserId();

        SysUser user = userAssembly.toUser(query);
        user.addExt("adminId", adminUserId);
        user.addExt("currentUserId", SecurityUtils.getUserId());

        List<SysUser> userList = userMapper.listUser(user);
        return userList.stream().map(userAssembly::toUserResp).collect(Collectors.toList());
    }

    @Override
    @FieldSensitiveHandle
    public IPage<UserResp> pageUser(UserPageReq req) {
        SysUser user = userAssembly.toUser(req);

        user.addExt("tenantId", TenantContext.get().getTenantId());
        LambdaQueryWrapper<SysUser> lqw = SysUser.lqw().eq(StringUtils.isNotEmpty(req.getUserName()), SysUser::getUserName, req.getUserName())
                .eq(StringUtils.isNotEmpty(req.getStatus()), SysUser::getStatus, req.getStatus())
                .eq(StringUtils.isNotEmpty(req.getDelFlag()), SysUser::getDelFlag, req.getDelFlag())
                .orderByDesc(BasePlusPO::getCreatedAt);

        IPage<SysUser> pageData = userMapper.selectPage(PageUtil.to(req), lqw);
        return PageUtil.to(pageData, e -> userAssembly.toUserResp(e));
    }

    @Override
    @FieldSensitiveHandle
    public UserResp getUserById(Long userId) {
        SysUser user = userMapper.selectById(userId);
        Map<Long, List<SysRole>> roleMap = roleManager.listRoleByUserIds(Collections.singletonList(userId));
        UserResp userResp = userAssembly.toUserResp(user);
        List<SysRole> roleList = roleMap.getOrDefault(userId, Collections.emptyList());
        userResp.setRoleList(roleList.stream().map(roleAssembly::toRoleResp).collect(Collectors.toList()));
        return userResp;
    }

    @Override
    public SysUser getUserByAccount(String account) {
        LambdaQueryWrapper<SysUser> wrappers = Wrappers.lambdaQuery();
        if (RegexUtils.checkEmail(account)) {
            wrappers.eq(SysUser::getEmail, account).ge(SysUser::getUserName, "").ge(SysUser::getPhone, "");
        } else if (RegexUtils.checkPhone(account)) {
            wrappers.ge(SysUser::getEmail, "").ge(SysUser::getUserName, "").eq(SysUser::getPhone, account);
        }  else {
            wrappers.ge(SysUser::getEmail, "").eq(SysUser::getUserName, account).ge(SysUser::getPhone, "");
        }
        wrappers.eq(SysUser::getDelFlag, BoolEnum.FALSE.getCode());
        return userMapper.selectOne(wrappers);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveUser(UserSaveReq req) {
        Assert.notNull(req, "用户信息为空");

        List<Long> roleIdList = StringUtils.toList(req.getRoleIds(), ",", Long.class);

        // 检查角色
        roleManager.checkRole(roleIdList);

        List<Long> deptIdList = StringUtils.toList(req.getDeptIds(), ",", Long.class);

        SysUser user = userAssembly.toUser(req);

        CheckLoginParam.of(null, req.getPassword()).passwordLength().throwError((errorEnum) -> {
            throw new BizException(errorEnum);
        });
        
        // 新增用户信息
        userManager.saveUser(user);
        
        // 分配角色
        authorizeService.assignUserRole(user.getUserId(), roleIdList);

        // 分配用户部门
        authorizeService.assignUserDept(user.getUserId(), deptIdList);

        // 插入用户本地授权表
        LocalUser localUser = LocalUser.builder().userName(user.getUserName())
                .userId(user.getUserId()).appType(AppType.ADMIN_CONSOLE.getCode())
                .password(req.getPassword()).email(user.getEmail()).phone(req.getPhone()).build();
        authApi.saveLocalUser(localUser);
        return user.getUserId();
    }


    @Override
    @Transactional
    @FieldUniqueCheck(type = OperationTypeEnum.UPDATE)
    public boolean updateById(SysUser user) {
        SysUser oldUser = userMapper.selectById(user.getUserId());
        if (Objects.isNull(oldUser)) {
            throw new BizException(AuthErrorEnum.USER_ACCOUNT_NOT_EXIST);
        }

        checkEmail(oldUser, user);
        checkPhone(oldUser, user);

        return userMapper.updateById(user) > 0;
    }

    @Override
    public boolean updateLastWorkspace(SysUser user) {
        if (Objects.isNull(user.getUserId())) {
            return false;
        }
        LambdaUpdateWrapper<SysUser> lqw = SysUser.luw()
            .eq(SysUser::getUserId, user.getUserId())
            .set(Objects.nonNull(user.getCurrentDeptId()), SysUser::getCurrentDeptId, user.getCurrentDeptId())
            .set(Objects.nonNull(user.getCurrentTenantId()), SysUser::getCurrentTenantId, user.getCurrentTenantId());
        return userManager.update(lqw);
    }

    @Override
    public boolean updateUserStatus(SysUser user) {
        this.checkUserAllowed(user.getUserId());
        return userMapper.updateById(user) > 0;
    }

    @Override
    @Transactional
    public void updateBatchUserStatus(UserStatusUpdateReq req) {
        List<Long> userIds = StringUtils.toList(req.getIds(), ",", Long.class);
        userManager.checkUserAllowed(userIds);

        LambdaQueryWrapper<SysUser> lqw =SysUser.lqw().in(SysUser::getUserId, userIds);
        SysUser user =  new SysUser();
        user.setStatus(req.getStatus());
        if (userMapper.update(user, lqw) != userIds.size()) {
            throw new BizException("用户状态更新失败, 请联系管理员");
        }
        // 更新状态之后, 删除用户登录状态
        userIds.forEach(authApi::removeAccessTokenByUserId);

        List<LocalUser> localUserList = userIds.stream()
                .map(e -> LocalUser.builder().userId(e).status(req.getStatus()).build())
                .collect(Collectors.toList());
        authApi.batchUpdateLocalUser(localUserList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateUserProfile(UserProfileUpdateReq req) {
        Long userId = SecurityUtils.getUserId();

        SysUser dbEntity = this.userMapper.selectById(userId);
        if (Objects.isNull(dbEntity)) {
            throw new BizException(AuthErrorEnum.USER_ACCOUNT_NOT_EXIST);
        }

        // 更新用户
        SysUser user = userAssembly.toUser(req);
        checkEmail(dbEntity, user);
        checkPhone(dbEntity, user);

        LoginUser loginUser = authApi.getLoginUser();

        LambdaUpdateWrapper<SysUser> wrapper = SysUser.luw().eq(SysUser::getUserId, userId);
        boolean result = userMapper.update(user, wrapper) > 0;
        if (result) {
            // 更新缓存用户信息
            UserAccount account = loginUser.getAccount();
            account.setEmail(req.getEmail());
            account.setPhone(req.getPhone());
            loginUser.setAccount(account);
            authApi.updateLoginUser(loginUser);

            LocalUser localUser = LocalUser.builder().userId(loginUser.getUserId())
                    .email(req.getEmail()).phone(req.getPhone()).build();
            authApi.updateLocalUser(localUser);
            return true;
        }
        return false;
    }

    public void checkEmail(SysUser dbEntity, SysUser req) {
        // 避免保存脱敏的数据, 如果手机号或者邮箱修改过, 则条件肯定为false
        if (SensitiveDataUtils.email(dbEntity.getEmail()).equals(req.getEmail())) {
            req.setEmail(dbEntity.getEmail());
        }

        // 校验
        if (StringUtils.isNotEmpty(req.getEmail()) && !RegexUtils.checkEmail(req.getEmail())) {
            throw new BizException("非法邮箱格式");
        }
    }

    public void checkPhone(SysUser dbEntity, SysUser req) {
        // 避免保存脱敏的数据, 如果手机号或者邮箱修改过, 则条件肯定为false
        if (SensitiveDataUtils.mobilePhone(dbEntity.getPhone()).equals(req.getPhone())) {
            req.setPhone(dbEntity.getPhone());
        }

        if (StringUtils.isNotEmpty(req.getPhone()) && !RegexUtils.checkPhone(req.getPhone())) {
            throw new BizException("非法手机号格式");
        }
    }


    @Override
    public boolean deleteLogicUserById(Long userId) {
        checkUserAllowed(userId);
        SysUser user = userManager.getById(userId);
        if (Objects.isNull(user)) {
            return true;
        }

        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setDelFlag(BoolEnum.TRUE.getCode());
        authApi.removeAccessTokenByUserId(userId);
        if (userMapper.updateById(sysUser) <= 0) {
            return false;
        }
        LocalUser localUser = LocalUser.builder()
                .userId(userId).status(UserCons.Status.DELETED).build();
        authApi.updateLocalUser(localUser);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUserByIds(List<Long> userIds) {
        String tenantId = TenantContext.get().getTenantId();
        userManager.checkUserAllowed(userIds);
        userMapper.deleteBatchIds(userIds);

        LambdaQueryWrapper<SysUserDeptRel> deptRelPOLqw = SysUserDeptRel.lqw();
        deptRelPOLqw.in(SysUserDeptRel::getUserId, userIds);
        userDeptRelMapper.delete(deptRelPOLqw);

        LambdaQueryWrapper<SysUserRole> userRoleRelPOLqw = SysUserRole.lqw();
        userRoleRelPOLqw.in(SysUserRole::getUserId, userIds);
        userRoleRelMapper.delete(userRoleRelPOLqw);

        authApi.batchDeleteLocalUser(tenantId, userIds);

        // 发送刷新消息. 注意，需要事务提交后，在进行发送刷新消息。如果数据库出现异常提交失败, 这时做刷新缓存或者通知处理, 很有可能有问题
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                // 发出删除用户消息-删除其余数据
                userIds.forEach(mqManager::sendDeleteUserMsg);
            }
        });
    }

    @Override
    public void checkUserAllowed(Long userId) {
        userManager.checkUserAllowed(userId);
    }

    /**
     * 导入用户数据
     *
     * @param userList 用户数据列表
     * @param isUpdateSupport 是否更新支持，如果已存在，则进行更新数据
     * @param operName 操作用户
     * @return 结果
     */
    @Override
    public String importUser(List<UserSaveReq> userList, Boolean isUpdateSupport, String operName) {
        if (StringUtils.isNull(userList) || userList.size() == 0) {
            throw new BizException("导入用户数据不能为空！");
        }
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        ConfigResp config = configService.getConfig(new ConfigGetReq("sys.user.initPassword"));
        if (Objects.isNull(config)) {
            throw new BizException("尚未定义 sys.user.initPassword 配置");
        }
        String password = config.getConfigValue();
        for (UserSaveReq user : userList) {
            try {
                // 验证是否存在这个用户
                SysUser u = userMapper.selectUserByUsername(user.getUserName());
                if (StringUtils.isNull(u)) {
                    user.setPassword(SecurityUtils.encryptPassword(password));
                    this.saveUser(user);
                    successNum++;
                    successMsg.append("<br/>").append(successNum).append("、账号 ").append(user.getUserName()).append(" 导入成功");
                } else if (isUpdateSupport) {
                    // TODO 先不实现
//                    this.updateById(user);
                    successNum++;
                    successMsg.append("<br/>").append(successNum).append("、账号 ").append(user.getUserName()).append(" 更新成功");
                } else {
                    failureNum++;
                    failureMsg.append("<br/>").append(failureNum).append("、账号 ").append(user.getUserName()).append(" 已存在");
                }
            } catch (Exception e) {
                failureNum++;
                String msg = "<br/>" + failureNum + "、账号 " + user.getUserName() + " 导入失败：";
                failureMsg.append(msg).append(e.getMessage());
                log.error(msg, e);
            }
        }
        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确，错误如下：");
            throw new BizException(failureMsg.toString());
        } else {
            successMsg.insert(0, "恭喜您，数据已全部导入成功！共 " + successNum + " 条，数据如下：");
        }
        // TODO 要将用户账号信息插入到本地授权表中
        return successMsg.toString();
    }

    @Override
    @Transactional
    public void recoverRecycleBin(UserRecycleBinReq in) {
        List<Long> userIds = StringUtils.toList(in.getUserIds(), ",", Long.class);
        LambdaQueryWrapper<SysUser> lqw = new LambdaQueryWrapper<>();
        lqw.in(SysUser::getUserId, userIds);

        SysUser sysUser = new SysUser();
        sysUser.setDelFlag(BoolEnum.FALSE.getCode());
        userMapper.update(sysUser, lqw);
    }

    @Override
    @Transactional
    public void register(UserRegisterReq req) {
        // 获取应用配置
        SysTenant tenant = tenantManager.getById(req.getTenantId());
        if (Objects.isNull(tenant)) {
            throw new BizException("当前域名未绑定任何租户");
        }
        TenantContext.get().setTenantId(tenant.getId());

        String paramConfig = Optional.ofNullable(sysConfigService.getConfig(new ConfigGetReq(ConfigKeyEnum.USER_REGISTER.getKey())))
                .map(ConfigResp::getConfigValue).orElse("");
        UserRegisterParam params = BaseConfigParam.jsonToObject(ConfigKeyEnum.USER_REGISTER, paramConfig);
        if (Objects.nonNull(params) && !params.isEnableMgt()) {
            throw new BizException("注册功能已被关闭");
        }

        CheckLoginParam.of(null, req.getPassword()).passwordLength().throwError((errorEnum) -> {
            throw new BizException(errorEnum);
        });
        // 校验验证码
        CheckNoAuthUserCaptchaReq codeCheckRQ = CheckNoAuthUserCaptchaReq.builder().toUser(req.getPhone())
                .verifyCode(req.getSmsCode())
                .taskCode(NoticeCodeEnum.REGISTER_VERIFY.getCode()).toUser(req.getPhone()).build();

//        msgPushApi.checkMessage(codeCheckRQ);

        SysUser user = userAssembly.toUser(req);
        user.setSex(SexEnum.UNKNOWN.getCode()).setStatus(UserCons.Status.NORMAL)
                .setDelFlag(BoolEnum.FALSE.getCode()).setNickName(RandomUtil.randomString(16));

        // 新增用户信息
        Assert.isTrue(userManager.saveUser(user), new BizException("注册失败, 请联系管理员!!"));

        LocalUser localUser = LocalUser.builder().userName(user.getUserName())
                .userId(user.getUserId()).appType(AppType.ADMIN_CONSOLE.getCode())
                .password(req.getPassword()).email(user.getEmail()).phone(req.getPhone()).build();
        authApi.saveLocalUser(localUser);
    }

    @Override
    public void deleteLogicUser(LoginUserDeleteReq req) {
        Set<Long> removeUserIds = new HashSet<>();
        if (Objects.nonNull(req.getUserId())) {
            removeUserIds.add(req.getUserId());
        }
        removeUserIds.forEach(authApi::removeAccessTokenByUserId);
    }

    @Override
    public List<Long> listUserIdsByRoleId(Long roleId) {
        return userManager.listUserIdsByRoleIds(Collections.singletonList(roleId));
    }

    @Override
    public List<UserResp> listUserByIds(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return Collections.emptyList();
        }
        return userManager.list(SysUser.lqw().in(SysUser::getUserId, userIds)).stream().map(userAssembly::toUserResp)
                .collect(Collectors.toList());
    }

    /**
     * 获取用户信息, 步骤
     */
    @Override
    public LoginUser getLoginUser(LoginUserGetReq req) {
        SysUser user = userManager.getById(req.getUserId());

        if (StringUtils.isNull(user)) {
            throw new BizException(AuthErrorEnum.USER_ACCOUNT_NOT_EXIST);
        }

        return new LoginUser().setUserName(user.getUserName()).setStatus(user.getStatus()).setUserId(user.getUserId());
    }
}
